//
//  Lattice.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Lattice.h"
// Site Template Functions

//Lattice
void Lattice::initSquare(boost::array<int, DIM> const &latSize) {
	cordinationNumber_ = 2 * DIM;
	siteLattice_.init(latSize);
	XYBondLattice_.init(latSize);
	latSize_ = latSize;
	SiteIterIndex iter = siteLattice_.beginIndex();
	for (; iter != siteLattice_.endIndex(); iter++) {
		IndexType currentPos = iter.getIndex();
		//iter->siteIndex_ = currentPos;
		iter->XYTotalBonds_ = 0;
		for (int d = 0; d < DIM; d++) {
			// XY Bonds
			XYBondType& currentbondXY = XYBondLattice_(currentPos)[d];
			currentbondXY[0] = currentbondXY[1] = 0;
			// Update NN
			boost::array<int, DIM> nPos = currentPos;
			if (nPos[d] == (latSize_[d] - 1)) {
				nPos[d] = 0;
			} else
				nPos[d]++;
			SiteIter nIter = siteLattice_.iterByPos(nPos);
			// XY Bonds
			iter->XYBondArray_[d] = &currentbondXY;
			nIter->XYBondArray_[DIM + d] = &currentbondXY;
			// Nearest neighbor
			iter->nSiteArray_[d] = nIter;
			nIter->nSiteArray_[DIM + d] = iter.getIter();
		}
	}
}

void Lattice::init(boost::array<int, DIM> const &latSize) {
	this->initSquare(latSize);
}

bool Lattice::isClosed() const {
	return sectorType_ == ZSECTOR;
}
Lattice::SiteIter Lattice::getMasha() {
	return masha_;
}
Lattice::SiteIter Lattice::getIra() {
	return ira_;
}
void Lattice::setMasha(Lattice::SiteIter &iter) {
	masha_ = iter;
}
void Lattice::setIra(Lattice::SiteIter &iter) {
	ira_ = iter;
}
int Lattice::getCordinationNumber() const {
	return cordinationNumber_;
}

int Lattice::getNumOfSites() const {
	return siteLattice_.totalSize();
}

Lattice::SiteIter Lattice::getSite(int distanceFromStart) {
	return siteLattice_.begin() + distanceFromStart;
}
boost::array<int, DIM> Lattice::getSizes() {
	return latSize_;
}
int Lattice::getTotalBonds() const {
	return totalBonds_;
}
void Lattice::setToalBonds(int totalBonds) {
	totalBonds_ = totalBonds;
}
bool Lattice::testConfiguration(int &totalBonds) {
	totalBonds = 0;
	SiteIterIndex it = siteLattice_.beginIndex();
	for (; it != siteLattice_.endIndex(); it++) {
		int count = 0;
		int totalBondsXY = 0;
		Site::XYBondArray bondArrayXY = it->XYBondArray_;
		for (int d = 0; d < 2 * DIM; d++) {
			XYBondType XYBond = (*(bondArrayXY[d]));
			if (d < DIM) {
				count -= XYBond[0];
				assert(XYBond[0] >= 0);
				count += XYBond[1];
				assert(XYBond[1] >= 0);
			} else {
				count += XYBond[0];
				assert(XYBond[0] >= 0);
				count -= XYBond[1];
				assert(XYBond[1] >= 0);
			}
			totalBonds += XYBond[0] + XYBond[1];
			totalBondsXY += XYBond[0] + XYBond[1];
		}
		if (sectorType_ == GXY) {
			if (it.getIter() == this->getIra()) {
				count--;
				totalBondsXY++;
			}
			if (it.getIter() == this->getMasha()) {
				count++;
				totalBondsXY++;
			}
		}
		if (count != 0) {
			return false;
		}
		if (totalBondsXY / 2 != it->XYTotalBonds_) {
			return false;
		}
	}
	return true;
}
Lattice::XYBondArrayIter Lattice::XYBondBegin() {
	return XYBondLattice_.begin();
}
Lattice::XYBondArrayIter Lattice::XYBondEnd() {
	return XYBondLattice_.end();
}
Lattice::SiteIter Lattice::siteBegin() {
	return siteLattice_.begin();
}
Lattice::SiteIter Lattice::siteEnd() {
	return siteLattice_.end();
}
Lattice::SiteIterIndex Lattice::siteBeginIndex() {
	return siteLattice_.beginIndex();
}
Lattice::SiteIterIndex Lattice::siteEndIndex() {
	return siteLattice_.endIndex();
}
Lattice::XYBondArrayIterIndex Lattice::XYBondBeginIndex() {
	return XYBondLattice_.beginIndex();
}
Lattice::XYBondArrayIterIndex Lattice::XYBondEndIndex() {
	return XYBondLattice_.endIndex();
}
SectorType Lattice::getSectorType() const {
	return sectorType_;
}
void Lattice::setSectorType(SectorType sectorType) {
	sectorType_ = sectorType;
}
void Lattice::dump(string fileName, bool final, ofstream *outFile) {
	try {
		bool testConf = testConfiguration(totalBonds_);
		if (!testConf)
			throw "Bad Configuration Dumping";
	} catch (string &e) {
		*outFile << e;
		exit(1);
	}
	if (final)
		fileName += "/Latdat.zip";
	else
		fileName += "/LatdatTemp.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ofstream file(fileName.c_str(),
			std::ios::out | std::ios::binary | std::ios::trunc);
	io::filtering_ostream out;
	out.push(io::zlib_compressor());
	out.push(file);
	ar::text_oarchive oa(out);
//	ofstream file(fileName.c_str(), ios_base::out);
//	ar::text_oaromchive oa(file);
	oa << *this;

}
void Lattice::read(string fileName) {
	try {
		fileName += "/Latdat.zip";
		namespace io = boost::iostreams;
		namespace ar = boost::archive;
		ifstream file(fileName.c_str(), ios_base::in | ios_base::binary);
		io::filtering_istream in;
		in.push(io::zlib_decompressor());
		in.push(file);
		ar::text_iarchive oi(in);
		oi >> *this;
	} catch (string &s) {
		cout << s;
		exit(1);
	}
}

IndexType Lattice::iraIndex() {
	return siteLattice_.posByIter(ira_);
}
IndexType Lattice::mashaIndex() {
	return siteLattice_.posByIter(masha_);
}
